package sagroup.dracots.testing;

import java.util.Collection;

import java.util.LinkedList;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Logger;

import org.apache.commons.math.random.RandomData;
import org.apache.commons.math.random.RandomDataImpl;

import lime2.Location;
import lime2.TupleID;
import sagroup.dracots.DracoTS;
import sagroup.dracots.DracoTuple;
import sagroup.dracots.ShareTuple;
import sagroup.dracots.TupleType;
import sagroup.util.Util;

public class RemoteReadAgent2 implements Runnable {

	private long meaninterval;
	private Location remote;
	private TupleType type;
	private boolean timesup = false;
	private LinkedList<TupleID> haveRead = new LinkedList();
	private LinkedList<TupleID> unRead = new LinkedList();
	private double prob;
	private int newRead = 0;
	private int oldRead = 0;
	private int opCount = 0;

	private long begin;
	private long timing;

	private Logger LOG = Logger.getLogger(this.getClass().getName());

	public RemoteReadAgent2(String typeName, String host, String port,
			String meaninterval, String prob) {
		this.type = new TupleType(typeName);
		remote = Location.build(host, Integer.valueOf(port));
		this.meaninterval = Long.valueOf(meaninterval);
		this.prob = Double.valueOf(prob);
		if (this.meaninterval < 0 || this.prob < 0 || this.prob > 1) {
			throw new IllegalArgumentException("Parameters are illegal.");
		}

		

		Timer timer = new Timer();
		TimerTask tt = new TimerTask() {
			@Override
			public void run() {
				timesup = true;
				timing = (System.nanoTime() - begin) / 1000 / 1000 ;
			}
		};
		timer.schedule(tt, AgentUtil.expTime);
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			DracoTS dts = DracoTS.get("testing");
			ListTuple template = ListTuple.createListTemplate(type, remote);

			ListTuple list = null;

			int readcount = 0;
			while (list == null && readcount != 3) {
				list = (ListTuple) dts.rdp(template);
				Thread.sleep(1000);
				readcount++;
			}
			if (list == null)
				throw new Exception(this.toString()
						+ " can't read the tuples list.");
			LinkedList<TupleID> tupleIDs = list.getTupleList();
			constructSet(tupleIDs);

			this.begin = System.nanoTime();
			while (!timesup) {

				TupleID tid = randomID();
				ShareTuple temp = ShareTuple.createVIDTemplate(tid);
				DracoTuple dt = dts.rdp(temp);
//				if (dt != null)
//					System.out.println(Thread.currentThread().getName()
//							+ " has read " + dt.getVID());
				opCount++;
				long sleeptime = (long) (randomData.nextExponential(1) * this.meaninterval);
				Thread.sleep(sleeptime);
			}
			System.out.println(this.toString() + " Agent finishs all work.");

		} catch (Exception ex) {
			System.out.println("Exception occurs : "
					+ Util.getExceptionDetail(ex));
			System.out
					.println(this.toString() + " Agent has been interrupted.");
		} finally {
			LOG.info("Agent ��Ϣ : " + this.toString() + " " + this.getInfo());
		}

	}

	private RandomData randomData = new RandomDataImpl();
	private RandomData randomProb = new RandomDataImpl();
	private RandomData randomGet1 = new RandomDataImpl();
	private RandomData randomGet2 = new RandomDataImpl();

	private TupleID randomID() {

		TupleID tid = null;
		double random = randomProb.nextUniform(0, 1);

		if ((random >= 1 - prob && unRead.size() > 0) || haveRead.size() == 0) {
			tid = (TupleID) randomGet1.nextSample(unRead, 1)[0];
			unRead.remove(tid);
			haveRead.add(tid);
			//System.out.println("new tuple, total " + (++this.newRead));
		} else if (haveRead.size() > 0) {
			tid = (TupleID) randomGet2.nextSample(haveRead, 1)[0];
			//System.out.println("old tuple, total " + (++this.oldRead));
		}

		return tid;

	}

	private void constructSet(Collection<TupleID> ret) {

		for (TupleID t : ret)
			unRead.add(t);

	}

	public String toString() {
		String id = "Agent Read2" + ":"
				+ Thread.currentThread().getName();
		String target = "Target type : " + type + " ; Target remote : "
				+ remote + "; Read New Tuple Prob : " + this.prob;
		return id + ", " + target;
	}

	private String getInfo() {
		String info = "Operation times : " + this.opCount + " Timing : "
				+ this.timing + ". Average : "
				+ ((this.opCount != 0) ? this.timing / this.opCount : null);
		return info;
	}

}
